package load_balance

import (
	"fmt"
	"log"
	"strings"

	"github.com/youkelike/gateway/core/lib"
)

type LoadBalanceEtcdConf struct {
	observers    []Observer
	path         string
	confIpWeight map[string]string
	activeList   []string
	format       string
	closeChan    chan bool
}

// path 服务名，
// format 参数用于构造不同协议类型的 url，
func NewLoadBalanceEtcdConf(path string, format string) (*LoadBalanceEtcdConf, error) {
	resolver, err := lib.GetEtcdResolverOr()
	if err != nil {
		log.Fatalf("lib.NewLoadBalanceEtcdConf error: %v", err)
	}

	elist, err := resolver.GetServerListByPrefix(path)
	log.Printf("path: %v, elist: %v\n", path, elist)
	if err != nil {
		return nil, err
	}
	iplist, ipWeightMap := IpListSplit(elist)

	mConf := &LoadBalanceEtcdConf{
		format:       format,
		activeList:   iplist,
		confIpWeight: ipWeightMap,
		path:         path,
	}
	mConf.WatchConf()
	return mConf, nil
}

func (s *LoadBalanceEtcdConf) Attach(o Observer) {
	s.observers = append(s.observers, o)
}

func (s *LoadBalanceEtcdConf) NotifyAllObservers() {
	confList := s.GetConf()
	for _, obs := range s.observers {
		obs.Update(confList)
	}
}

func (s *LoadBalanceEtcdConf) GetConf() []string {
	confList := []string{}
	for _, ip := range s.activeList {
		weight, ok := s.confIpWeight[ip]
		if !ok {
			weight = "50" //默认weight
		}
		confList = append(confList, fmt.Sprintf(s.format, ip)+","+weight)
	}
	return confList
}

// WatchConf 更新配置时，通知监听者也更新
func (s *LoadBalanceEtcdConf) WatchConf() {
	resolver, err := lib.GetEtcdResolverOr()
	if err != nil {
		log.Fatalf("public.GetEtcdResolverOr error: %v", err)
	}

	// fmt.Println("etcd watchConf: ", s.path)
	chanList, chanErr := resolver.WatchServerListByPrefix(s.path)
	go func() {
		for {
			select {
			case <-s.closeChan:
				return
			case changeErr := <-chanErr:
				log.Printf("etcd changeErr: %v\n", changeErr)
			case changedList := <-chanList:
				log.Println("etcd watch node changed: ")
				s.UpdateConf(changedList)
			}
		}
	}()
}

// UpdateConf 更新配置时，通知监听者也更新
func (s *LoadBalanceEtcdConf) UpdateConf(conf []string) {
	s.activeList, s.confIpWeight = IpListSplit(conf)
	s.NotifyAllObservers()
}

func IpListSplit(data []string) (ipList []string, ipWeightMap map[string]string) {
	ipWeightMap = make(map[string]string, len(data))
	for _, s := range data {
		arr := strings.Split(s, ",")
		if len(arr) != 2 {
			continue
		}
		ipList = append(ipList, arr[0])
		ipWeightMap[arr[0]] = arr[1]
	}
	return
}
